use anyhow::{anyhow, Result};
use calamine::{Data, Range, Reader, Xls, Xlsx};
use std::{collections::HashMap, fs::File, io::BufReader, path::Path};

pub enum Spreadsheet {
    Xls(Xls<BufReader<File>>),
    Xlsx(Xlsx<BufReader<File>>),
}

pub trait CswWorkbook {
    fn get_sheet_content_to_vec(&mut self, sheet_name: &str) -> Result<Vec<Vec<String>>>;
    fn get_sheet_content_to_map(
        &mut self,
        sheet_name: &str,
    ) -> Result<Vec<HashMap<String, String>>>;
}

impl Spreadsheet {
    pub fn new<P: AsRef<Path>>(path: P) -> Result<Self> {
        match path.as_ref().extension().and_then(|ext| ext.to_str()) {
            Some("xls") => {
                let xls_file = calamine::open_workbook::<Xls<_>, &Path>(path.as_ref())?;
                Ok(Spreadsheet::Xls(xls_file))
            }
            Some("xlsx") => {
                let xlsx_file = calamine::open_workbook::<Xlsx<_>, &Path>(path.as_ref())?;
                Ok(Spreadsheet::Xlsx(xlsx_file))
            }
            _ => Err(anyhow!("Unsupported file format")),
        }
    }

    fn get_range_data(&mut self, sheet_name: &str) -> Result<Range<Data>> {
        match self {
            Spreadsheet::Xls(xls) => {
                let sheet_data = xls.worksheet_range(sheet_name)?;
                Ok(sheet_data)
            }
            Spreadsheet::Xlsx(xlsx) => {
                let sheet_data = xlsx.worksheet_range(sheet_name)?;
                Ok(sheet_data)
            }
        }
    }
    pub fn get_sheet_names(&mut self) -> Result<Vec<String>> {
        match self {
            Spreadsheet::Xls(xls) => Ok(xls.sheet_names().to_vec()),
            Spreadsheet::Xlsx(xlsx) => Ok(xlsx.sheet_names().to_vec()),
        }
    }
}
impl CswWorkbook for Spreadsheet {
    fn get_sheet_content_to_vec(&mut self, sheet_name: &str) -> Result<Vec<Vec<String>>> {
        let sheet_data = self.get_range_data(sheet_name)?;
        let data_vec = sheet_data
            .rows()
            .map(|row_data| {
                row_data
                    .iter()
                    .map(|cell_data| cell_data.to_string())
                    .collect::<Vec<_>>()
            })
            .collect::<Vec<_>>();
        Ok(data_vec)
    }

    fn get_sheet_content_to_map(
        &mut self,
        sheet_name: &str,
    ) -> Result<Vec<HashMap<String, String>>> {
        let sheet_data = self.get_range_data(sheet_name)?;
        let title = sheet_data
            .rows()
            .next() // 获取第一行
            .map(|row| {
                // 对这一行进行映射操作
                row.into_iter() // 消耗行迭代器
                    .map(|val| val.to_string()) // 将每个值转换为字符串
                    .collect::<Vec<_>>() // 收集结果到 Vec<String>
            })
            .ok_or_else(|| anyhow!("No data found in sheet's first row"))?;
        let data_map = sheet_data
            .rows()
            .skip(1)
            .map(|row_data| {
                row_data
                    .into_iter()
                    .enumerate()
                    .map(|(i, val)| {
                        let key = title[i].clone();
                        let value = val.to_string();
                        (key, value)
                    })
                    .collect::<HashMap<_, _>>()
            })
            .collect::<Vec<_>>();
        Ok(data_map)
    }
}
